package allod.camera

import actors._
import actors.Actor._
import java.awt.{Color, Image}
import robbie.imageprocessing.{GaussianBlur, CIELabColorSpace}
import java.awt.image.BufferedImage
import allod.som.functions.metric.EuclideanMetric
import allod.som.upgma.UPGMA
import allod.gui.som.Dendrogram
import allod.som.initializer.{RandomInitializer, SampleInitializer}
import allod.som.{Metric, Tuner, Lattice, LabeledLattice}
import allod.som.input.{InputSource, ImageInputSource}
import allod.som.functions.topological.Gaussian
import allod.som.functions.step.ExponentialStepFunction
import allod.som.LabeledLattice.Marker
import allod.concurrent.{Response, Request, WorkerPool}

/**
 * Created by IntelliJ IDEA.
 * User: allod
 * Date: 4 трав 2010
 * Time: 23:13:15
 */

class Handler(viewer: Viewer) extends Actor {
  val worker = new WorkerPool(viewer, 2)
  var enableProcessing: Boolean = false

  var labeledLattice: LabeledLattice[Color] = null
  private var dendrogram: Dendrogram = null
  private var imageProcessor: ImageConverter = null
  private val blur = new GaussianBlur()
  private val colorSpace = new CIELabColorSpace()

  def act() {
    loop {
      react {
        case image: BufferedImage =>
          if (self.mailboxSize == 0) {
            if (enableProcessing) {
              val blurredImage = blur.gaussianBlur(image, 1)
              def action(img: BufferedImage): BufferedImage =
                imageProcessor.convert(img, labeledLattice)

              worker.coordinator ! Request(this, image, blurredImage, action)
            } else {
              val blankImage = new BufferedImage(image.getWidth, image.getHeight, BufferedImage.TYPE_INT_RGB)
              viewer ! Response(self, image, blankImage)
            }
          }
      }
    }
  }

  private def createDendrogram(lattice: Lattice) = {
    val clusters = UPGMA.process(lattice)
    val dendrogram = new Dendrogram(clusters)
    dendrogram
  }

  private def createLattice(settings: Map[String, Double], source: InputSource) = {
    val metric: Metric = EuclideanMetric
    val initializerType = settings("initializer").toInt
    val rows = settings("rows").toInt
    val columns = settings("columns").toInt
    val initializer = initializerType match {
      case 0 => new SampleInitializer(source, metric)
      case 1 => new RandomInitializer(columns * rows, metric)
      case _ => throw new IllegalArgumentException("Unsupported initializer")
    }
    val builder = new Lattice.Builder(columns, rows, initializer)

    val topologyType = settings("topology").toInt
    val topologicalFun = topologyType match {
      case 0 =>
        val sigma0 = settings("sigma0")
        val tau1 = settings("tau1")
        new Gaussian(sigma0, tau1)
      case _ =>
        throw new IllegalArgumentException("Unsupported neiberhood function")
    }

    val stepType = settings("stepType").toInt
    val stepFun = stepType match {
      case 0 =>
        val eta0 = settings("eta0")
        val tau2 = settings("tau2")
        new ExponentialStepFunction(eta0, tau2)
      case _ =>
        throw new IllegalArgumentException("Unsupported step function")
    }

    val tuner = new Tuner(builder, topologicalFun, stepFun, source)
    val iterationsCount = settings("iterations").toInt
    val lattice = tuner.tune(iterationsCount)

    lattice
  }

  def buildLabeledLattice(settings: Map[String, Double], frames: List[Image]) {
    val source = createInputSource(frames)
    val lattice = createLattice(settings, source)

    dendrogram = createDendrogram(lattice)
    imageProcessor = new ImageConverter(lattice, colorSpace)

    val colorLatticeMarker = new Marker[Color](lattice)
    val initClusters = dendrogram.getSelectedClusters
    val colorGenerator = LabeledLattice.clusterColorCreator(initClusters)(_)
    labeledLattice = colorLatticeMarker.createLabeledLattice(initClusters, colorGenerator)
    enableProcessing = true
  }

  def updateLabeledLattice() {
    val colorLatticeMarker = new Marker[Color](labeledLattice.peer)
    val clusters = dendrogram.getSelectedClusters
    val colorGenerator = LabeledLattice.clusterColorCreator(clusters)(_)
    labeledLattice = colorLatticeMarker.createLabeledLattice(clusters, colorGenerator)
  }

  def createInputSource(frames: List[Image]) = {
    val blockSplitSize = 5
    val blurredPicture = blur.gaussianBlur(frames.head.asInstanceOf[BufferedImage], 1)
    new ImageInputSource(blurredPicture, colorSpace, EuclideanMetric, blockSplitSize)
  }

  def getLabeledLattice: LabeledLattice[Color] = labeledLattice

  def getDendrogram: Dendrogram = dendrogram
}